/*
 * Title:        CloudScheduler Toolkit
 * Description:  a Toolkit for Modeling and Simulation of Job Scheduling and Resource Provisioning in Cloud System
 * Licence:      GPL - http://www.gnu.org/copyleft/gpl.html
 * Author:       Fuhui Wu
 *
 * Copyright (c) 2013-2014, The National University of Defense Technology, China
 */

package org.nudt.jCloud.infrastructure;

import java.util.ArrayList;
import java.util.List;

import org.nudt.jCloud.simulation.CloudSchedSIM;
import org.nudt.jCloud.simulation.VirtSlot;
import org.nudt.jCloud.util.Log;

public class Host {
	private int id;
	private List<Pe> peList;
	private RamProvisioner ramProvisioner;
	private BwProvisioner bwProvisioner;
	private StorageProvisioner storageProvisioner;
	private List<Slot> slotList;

	public Host(int id,
			List<Pe> peList,
			RamProvisioner ramProvisioner,
			BwProvisioner bwProvisioner,
			StorageProvisioner storageProvisioner) {
		setId(id);
		setPeList(peList);
		setRamProvisioner(ramProvisioner);
		setBwProvisioner(bwProvisioner);
		setStorageProvisioner(storageProvisioner);
		setSlotList(new ArrayList<Slot>());
	}
	

	public void reset() {
		slotList.clear();
		for(int i=0; i<peList.size(); ++i){
			peList.get(i).setBusy(false);
		}
		ramProvisioner.reset();
		bwProvisioner.reset();
		storageProvisioner.reset();
	}
	
	public Slot provideSlot(SlotType slotType) {
		if(getAvailablePes().size()<slotType.getPeCount() || 
				getAvailableRam()<slotType.getRam() || 
				getAvailableBw()<slotType.getBw() ||
				getAvailableStorage()<slotType.getStorage()){
			return null;
		}else {
			List<Pe> pes = new ArrayList<Pe>();
			int count = 0;
			for(int i=0; i<peList.size(); ++i){
				if(peList.get(i).isBusy()==false){
					pes.add(peList.get(i));
					peList.get(i).setBusy(true);
					count ++;
				}
				if(count >= slotType.getPeCount()){
					break;
				}
			}
			ramProvisioner.setAvailableRam(getAvailableRam()-slotType.getRam());
			bwProvisioner.setAvailableBw(getAvailableBw()-slotType.getBw());
			storageProvisioner.setAvailableStorage(getAvailableStorage()-slotType.getStorage());
			Slot slot = new Slot(this, pes, slotType.getRam(), slotType.getBw(), slotType.getStorage());
			getSlotList().add(slot);
			int state = 0;
			if(getSlotList().size()==1){
				state = 1;
			}
			Log.hostLog(CloudSchedSIM.getClock(), getId(), state, 0, slot.getPeList().size(), slot.getRam());
			return slot;
		}
	}
	
	public void releaseSlot(Slot slot){
		for(int i=0; i<slot.getPeList().size(); ++i){
			slot.getPeList().get(i).setBusy(false);
		}
		ramProvisioner.setAvailableRam(getAvailableRam()+slot.getRam());
		bwProvisioner.setAvailableBw(getAvailableBw()+slot.getBw());
		storageProvisioner.setAvailableStorage(getAvailableStorage()+slot.getStorage());
		getSlotList().remove(slot);
		int state = 0;
		if(getSlotList().size()==0){
			state = 2;
		}
		Log.hostLog(CloudSchedSIM.getClock(), getId(), state, 1, slot.getPeList().size(), slot.getRam());
	}
	
	public VirtSlot provideVirtSlot(SlotType slotType, List<VirtSlot> virtSlotList){
		int peCount = slotType.getPeCount();
		int ram = slotType.getRam();
		int bw = slotType.getBw();
		int storage = slotType.getStorage();
		if(virtSlotList != null){
			for(int i=0; i<virtSlotList.size(); ++i){
				peCount += virtSlotList.get(i).getSlotType().getPeCount();
				ram+= virtSlotList.get(i).getSlotType().getRam();
				bw += virtSlotList.get(i).getSlotType().getBw();
				storage += virtSlotList.get(i).getSlotType().getStorage();
			}
		}
		if(getAvailablePes().size()<peCount ||
				getAvailableRam()<ram ||
				getAvailableStorage()<storage ||
				getAvailableBw()<bw){
			return null;
		}
		return new VirtSlot(slotType, this, null);
	}
	
	public List<Pe> getAvailablePes(){
		List<Pe> availablePes = new ArrayList<Pe>();
		for(int i=0; i<peList.size(); ++i){
			if(peList.get(i).isBusy() == false) availablePes.add(peList.get(i));
		}
		return availablePes;
		
	}
	
	public boolean isBusy(){
		if(getSlotList().size()<=0){
			return false;
		}else{
			return true;
		}
	}
	
	@Override
	public int hashCode(){
		return getId();
	}
	
	@Override
	public boolean equals(Object o){
		if(o == null){
			return false;
		}
		if(!(o instanceof Host)){
			return false;
		}
		Host host = (Host)o;
		if(this.id==host.getId()){
			return true;
		}else{
			return false;
		}
	}
	
	public int getAvailableRam(){
		return ramProvisioner.getAvailableRam();
	}
	
	public int getAvailableBw(){
		return bwProvisioner.getAvailableBw();
	}
	
	public int getAvailableStorage(){
		return storageProvisioner.getAvailableStorage();
	}

	/**
	 * @return the peList
	 */
	public List<Pe> getPeList() {
		return peList;
	}

	/**
	 * @param peList the peList to set
	 */
	public void setPeList(List<Pe> peList) {
		this.peList = peList;
	}

	/**
	 * @return the ramProvisioner
	 */
	public RamProvisioner getRamProvisioner() {
		return ramProvisioner;
	}

	/**
	 * @param ramProvisioner the ramProvisioner to set
	 */
	public void setRamProvisioner(RamProvisioner ramProvisioner) {
		this.ramProvisioner = ramProvisioner;
	}

	/**
	 * @return the bwProvisioner
	 */
	public BwProvisioner getBwProvisioner() {
		return bwProvisioner;
	}

	/**
	 * @param bwProvisioner the bwProvisioner to set
	 */
	public void setBwProvisioner(BwProvisioner bwProvisioner) {
		this.bwProvisioner = bwProvisioner;
	}

	/**
	 * @return the storageProvisioner
	 */
	public StorageProvisioner getStorageProvisioner() {
		return storageProvisioner;
	}

	/**
	 * @param storageProvisioner the storageProvisioner to set
	 */
	public void setStorageProvisioner(StorageProvisioner storageProvisioner) {
		this.storageProvisioner = storageProvisioner;
	}

	/**
	 * @return the id
	 */
	public int getId() {
		return id;
	}

	/**
	 * @param id the id to set
	 */
	public void setId(int id) {
		this.id = id;
	}
	
	public List<Slot> getSlotList() {
		return slotList;
	}

	public void setSlotList(List<Slot> slotList) {
		this.slotList = slotList;
	}

}
